home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / dejagnu.lha / dejagnu-1.0.1 / tcl / tclCkalloc.c < prev    next >
C/C++ Source or Header  |  1992-12-23  |  16KB  |  567 lines

  1. /* 
  2.  * tclCkalloc.c --
  3.  *    Interface to malloc and free that provides support for debugging problems
  4.  *    involving overwritten, double freeing memory and loss of memory.
  5.  *
  6.  * Copyright 1991 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  *
  15.  * This code contributed by Karl Lehenbauer and Mark Diekhans
  16.  *
  17.  */
  18.  
  19. #include "tclInt.h"
  20.  
  21. #define FALSE    0
  22. #define TRUE    1
  23.  
  24. #ifdef TCL_MEM_DEBUG
  25. #ifndef TCL_GENERIC_ONLY
  26. #include "tclUnix.h"
  27. #endif
  28.  
  29. #define GUARD_SIZE 8
  30.  
  31. struct mem_header {
  32.         long               length;
  33.         char              *file;
  34.         int                line;
  35.         struct mem_header *flink;
  36.         struct mem_header *blink;
  37.         unsigned char      low_guard[GUARD_SIZE];
  38.         char               body[1];
  39. };
  40.  
  41. static struct mem_header *allocHead = NULL;  /* List of allocated structures */
  42.  
  43. #define GUARD_VALUE  0341
  44.  
  45. /* static char high_guard[] = {0x89, 0xab, 0xcd, 0xef}; */
  46.  
  47. static int total_mallocs = 0;
  48. static int total_frees = 0;
  49. static int current_bytes_malloced = 0;
  50. static int maximum_bytes_malloced = 0;
  51. static int current_malloc_packets = 0;
  52. static int maximum_malloc_packets = 0;
  53. static int break_on_malloc = 0;
  54. static int trace_on_at_malloc = 0;
  55. static int  alloc_tracing = FALSE;
  56. static int  init_malloced_bodies = FALSE;
  57. #ifdef MEM_VALIDATE
  58.     static int  validate_memory = TRUE;
  59. #else
  60.     static int  validate_memory = FALSE;
  61. #endif
  62.  
  63.  
  64. /*
  65.  *----------------------------------------------------------------------
  66.  *
  67.  * dump_memory_info --
  68.  *     Display the global memory management statistics.
  69.  *
  70.  *----------------------------------------------------------------------
  71.  */
  72. static void
  73. dump_memory_info(outFile) 
  74.     FILE *outFile;
  75. {
  76.         fprintf(outFile,"total mallocs             %10d\n", 
  77.                 total_mallocs);
  78.         fprintf(outFile,"total frees               %10d\n", 
  79.                 total_frees);
  80.         fprintf(outFile,"current packets allocated %10d\n", 
  81.                 current_malloc_packets);
  82.         fprintf(outFile,"current bytes allocated   %10d\n", 
  83.                 current_bytes_malloced);
  84.         fprintf(outFile,"maximum packets allocated %10d\n", 
  85.                 maximum_malloc_packets);
  86.         fprintf(outFile,"maximum bytes allocated   %10d\n", 
  87.                 maximum_bytes_malloced);
  88. }
  89.  
  90. /*
  91.  *----------------------------------------------------------------------
  92.  *
  93.  * ValidateMemory --
  94.  *     Procedure to validate allocted memory guard zones.
  95.  *
  96.  *----------------------------------------------------------------------
  97.  */
  98. static void
  99. ValidateMemory (memHeaderP, file, line, nukeGuards)
  100.     struct mem_header *memHeaderP;
  101.     char              *file;
  102.     int                line;
  103.     int                nukeGuards;
  104. {
  105.     unsigned char *hiPtr;
  106.     int   idx;
  107.     int   guard_failed = FALSE;
  108.     int byte;
  109.     
  110.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  111.         byte = *(memHeaderP->low_guard + idx);
  112.         if (byte != GUARD_VALUE) {
  113.             guard_failed = TRUE;
  114.             fflush (stdout);
  115.         byte &= 0xff;
  116.             fprintf(stderr, "low guard byte %d is 0x%x  \t%c\n", idx, byte,
  117.                 (isprint(byte) ? byte : ' '));
  118.         }
  119.     }
  120.     if (guard_failed) {
  121.         dump_memory_info (stderr);
  122.         fprintf (stderr, "low guard failed at %lx, %s %d\n",
  123.                  memHeaderP->body, file, line);
  124.         fflush (stderr);  /* In case name pointer is bad. */
  125.         fprintf (stderr, "%d bytes allocated at (%s %d)\n", memHeaderP->length,
  126.         memHeaderP->file, memHeaderP->line);
  127.         panic ("Memory validation failure");
  128.     }
  129.  
  130.     hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
  131.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  132.         byte = *(hiPtr + idx);
  133.         if (byte != GUARD_VALUE) {
  134.             guard_failed = TRUE;
  135.             fflush (stdout);
  136.         byte &= 0xff;
  137.             fprintf(stderr, "hi guard byte %d is 0x%x  \t%c\n", idx, byte,
  138.                 (isprint(byte) ? byte : ' '));
  139.         }
  140.     }
  141.  
  142.     if (guard_failed) {
  143.         dump_memory_info (stderr);
  144.         fprintf (stderr, "high guard failed at %lx, %s %d\n",
  145.                  memHeaderP->body, file, line);
  146.         fflush (stderr);  /* In case name pointer is bad. */
  147.         fprintf (stderr, "%d bytes allocated at (%s %d)\n", memHeaderP->length,
  148.         memHeaderP->file, memHeaderP->line);
  149.         panic ("Memory validation failure");
  150.     }
  151.  
  152.     if (nukeGuards) {
  153.         memset ((char *) memHeaderP->low_guard, 0, GUARD_SIZE); 
  154.         memset ((char *) hiPtr, 0, GUARD_SIZE); 
  155.     }
  156.  
  157. }
  158.  
  159. /*
  160.  *----------------------------------------------------------------------
  161.  *
  162.  * Tcl_ValidateAllMemory --
  163.  *     Validates guard regions for all allocated memory.
  164.  *
  165.  *----------------------------------------------------------------------
  166.  */
  167. void
  168. Tcl_ValidateAllMemory (file, line)
  169.     char  *file;
  170.     int    line;
  171. {
  172.     struct mem_header *memScanP;
  173.  
  174.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
  175.         ValidateMemory (memScanP, file, line, FALSE);
  176.  
  177. }
  178.  
  179. /*
  180.  *----------------------------------------------------------------------
  181.  *
  182.  * Tcl_DumpActiveMemory --
  183.  *     Displays all allocated memory to stderr.
  184.  *
  185.  * Results:
  186.  *     Return TCL_ERROR if an error accessing the file occures, `errno' 
  187.  *     will have the file error number left in it.
  188.  *----------------------------------------------------------------------
  189.  */
  190. int
  191. Tcl_DumpActiveMemory (fileName)
  192.     char *fileName;
  193. {
  194.     FILE              *fileP;
  195.     struct mem_header *memScanP;
  196.     char              *address;
  197.  
  198.     fileP = fopen (fileName, "w");
  199.     if (fileP == NULL)
  200.         return TCL_ERROR;
  201.  
  202.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
  203.         address = &memScanP->body [0];
  204.         fprintf (fileP, "%8lx - %8lx  %7d @ %s %d", address,
  205.                  address + memScanP->length - 1, memScanP->length,
  206.                  memScanP->file, memScanP->line);
  207.         if (strcmp(memScanP->file, "tclHash.c") == 0 && memScanP->line == 518){
  208.         fprintf(fileP, "\t|%s|", ((Tcl_HashEntry *) address)->key.string);
  209.     }
  210.     (void) fputc('\n', fileP);
  211.     }
  212.     fclose (fileP);
  213.     return TCL_OK;
  214. }
  215.  
  216. /*
  217.  *----------------------------------------------------------------------
  218.  *
  219.  * Tcl_DbCkalloc - debugging ckalloc
  220.  *
  221.  *        Allocate the requested amount of space plus some extra for
  222.  *        guard bands at both ends of the request, plus a size, panicing 
  223.  *        if there isn't enough space, then write in the guard bands
  224.  *        and return the address of the space in the middle that the
  225.  *        user asked for.
  226.  *
  227.  *        The second and third arguments are file and line, these contain
  228.  *        the filename and line number corresponding to the caller.
  229.  *        These are sent by the ckalloc macro; it uses the preprocessor
  230.  *        autodefines __FILE__ and __LINE__.
  231.  *
  232.  *----------------------------------------------------------------------
  233.  */
  234. char *
  235. Tcl_DbCkalloc(size, file, line)
  236.     unsigned int size;
  237.     char        *file;
  238.     int          line;
  239. {
  240.     struct mem_header *result;
  241.  
  242.     if (validate_memory)
  243.         Tcl_ValidateAllMemory (file, line);
  244.  
  245.     result = (struct mem_header *)malloc((unsigned)size + 
  246.                               sizeof(struct mem_header) + GUARD_SIZE);
  247.     if (result == NULL) {
  248.         fflush(stdout);
  249.         dump_memory_info(stderr);
  250.         panic("unable to alloc %d bytes, %s line %d", size, file, 
  251.               line);
  252.     }
  253.  
  254.     /*
  255.      * Fill in guard zones and size.  Link into allocated list.
  256.      */
  257.     result->length = size;
  258.     result->file = file;
  259.     result->line = line;
  260.     memset ((char *) result->low_guard, GUARD_VALUE, GUARD_SIZE);
  261.     memset (result->body + size, GUARD_VALUE, GUARD_SIZE);
  262.     result->flink = allocHead;
  263.     result->blink = NULL;
  264.     if (allocHead != NULL)
  265.         allocHead->blink = result;
  266.     allocHead = result;
  267.  
  268.     total_mallocs++;
  269.     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
  270.         (void) fflush(stdout);
  271.         fprintf(stderr, "reached malloc trace enable point (%d)\n",
  272.                 total_mallocs);
  273.         fflush(stderr);
  274.         alloc_tracing = TRUE;
  275.         trace_on_at_malloc = 0;
  276.     }
  277.  
  278.     if (alloc_tracing)
  279.         fprintf(stderr,"ckalloc %lx %d %s %d\n", result->body, size, 
  280.                 file, line);
  281.  
  282.     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
  283.         break_on_malloc = 0;
  284.         (void) fflush(stdout);
  285.         fprintf(stderr,"reached malloc break limit (%d)\n", 
  286.                 total_mallocs);
  287.         fprintf(stderr, "program will now enter C debugger\n");
  288.         (void) fflush(stderr);
  289.         kill (getpid(), SIGINT);
  290.     }
  291.  
  292.     current_malloc_packets++;
  293.     if (current_malloc_packets > maximum_malloc_packets)
  294.         maximum_malloc_packets = current_malloc_packets;
  295.     current_bytes_malloced += size;
  296.     if (current_bytes_malloced > maximum_bytes_malloced)
  297.         maximum_bytes_malloced = current_bytes_malloced;
  298.  
  299.     if (init_malloced_bodies)
  300.         memset (result->body, 0xff, (int) size);
  301.  
  302.     return result->body;
  303. }
  304.  
  305. /*
  306.  *----------------------------------------------------------------------
  307.  *
  308.  * Tcl_DbCkfree - debugging ckfree
  309.  *
  310.  *        Verify that the low and high guards are intact, and if so
  311.  *        then free the buffer else panic.
  312.  *
  313.  *        The guards are erased after being checked to catch duplicate
  314.  *        frees.
  315.  *
  316.  *        The second and third arguments are file and line, these contain
  317.  *        the filename and line number corresponding to the caller.
  318.  *        These are sent by the ckfree macro; it uses the preprocessor
  319.  *        autodefines __FILE__ and __LINE__.
  320.  *
  321.  *----------------------------------------------------------------------
  322.  */
  323.  
  324. int
  325. Tcl_DbCkfree(ptr, file, line)
  326.     char *  ptr;
  327.     char     *file;
  328.     int       line;
  329. {
  330.     struct mem_header *memp = 0;  /* Must be zero for size calc */
  331.  
  332.     /*
  333.      * Since header ptr is zero, body offset will be size
  334.      */
  335.     memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
  336.  
  337.     if (alloc_tracing)
  338.         fprintf(stderr, "ckfree %lx %ld %s %d\n", memp->body, 
  339.                 memp->length, file, line);
  340.  
  341.     if (validate_memory)
  342.         Tcl_ValidateAllMemory (file, line);
  343.  
  344.     ValidateMemory (memp, file, line, TRUE);
  345.  
  346.     total_frees++;
  347.     current_malloc_packets--;
  348.     current_bytes_malloced -= memp->length;
  349.  
  350.     /*
  351.      * Delink from allocated list
  352.      */
  353.     if (memp->flink != NULL)
  354.         memp->flink->blink = memp->blink;
  355.     if (memp->blink != NULL)
  356.         memp->blink->flink = memp->flink;
  357.     if (allocHead == memp)
  358.         allocHead = memp->flink;
  359.     free((char *) memp);
  360.     return 0;
  361. }
  362.  
  363. /*
  364.  *--------------------------------------------------------------------
  365.  *
  366.  * Tcl_DbCkrealloc - debugging ckrealloc
  367.  *
  368.  *    Reallocate a chunk of memory by allocating a new one of the
  369.  *    right size, copying the old data to the new location, and then
  370.  *    freeing the old memory space, using all the memory checking
  371.  *    features of this package.
  372.  *
  373.  *--------------------------------------------------------------------
  374.  */
  375. char *
  376. Tcl_DbCkrealloc(ptr, size, file, line)
  377.     char *ptr;
  378.     unsigned int size;
  379.     char *file;
  380.     int line;
  381. {
  382.     char *new;
  383.  
  384.     new = Tcl_DbCkalloc(size, file, line);
  385.     memcpy((VOID *) new, (VOID *) ptr, (int) size);
  386.     Tcl_DbCkfree(ptr, file, line);
  387.     return(new);
  388. }
  389.  
  390. /*
  391.  *----------------------------------------------------------------------
  392.  *
  393.  * MemoryCmd --
  394.  *     Implements the TCL memory command:
  395.  *       memory info
  396.  *       memory display
  397.  *       break_on_malloc count
  398.  *       trace_on_at_malloc count
  399.  *       trace on|off
  400.  *       validate on|off
  401.  *
  402.  * Results:
  403.  *     Standard TCL results.
  404.  *
  405.  *----------------------------------------------------------------------
  406.  */
  407.     /* ARGSUSED */
  408. static int
  409. MemoryCmd (clientData, interp, argc, argv)
  410.     char       *clientData;
  411.     Tcl_Interp *interp;
  412.     int         argc;
  413.     char      **argv;
  414. {
  415.     char *fileName;
  416.  
  417.     if (argc < 2) {
  418.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  419.         argv[0], " option [args..]\"", (char *) NULL);
  420.     return TCL_ERROR;
  421.     }
  422.  
  423.     if (strcmp(argv[1],"trace") == 0) {
  424.         if (argc != 3) 
  425.             goto bad_suboption;
  426.         alloc_tracing = (strcmp(argv[2],"on") == 0);
  427.         return TCL_OK;
  428.     }
  429.     if (strcmp(argv[1],"init") == 0) {
  430.         if (argc != 3)
  431.             goto bad_suboption;
  432.         init_malloced_bodies = (strcmp(argv[2],"on") == 0);
  433.         return TCL_OK;
  434.     }
  435.     if (strcmp(argv[1],"validate") == 0) {
  436.         if (argc != 3)
  437.              goto bad_suboption;
  438.         validate_memory = (strcmp(argv[2],"on") == 0);
  439.         return TCL_OK;
  440.     }
  441.     if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
  442.         if (argc != 3) 
  443.             goto argError;
  444.         if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK)
  445.                 return TCL_ERROR;
  446.          return TCL_OK;
  447.     }
  448.     if (strcmp(argv[1],"break_on_malloc") == 0) {
  449.         if (argc != 3) 
  450.             goto argError;
  451.         if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK)
  452.                 return TCL_ERROR;
  453.         return TCL_OK;
  454.     }
  455.  
  456.     if (strcmp(argv[1],"info") == 0) {
  457.         dump_memory_info(stdout);
  458.         return TCL_OK;
  459.     }
  460.     if (strcmp(argv[1],"active") == 0) {
  461.         if (argc != 3) {
  462.         Tcl_AppendResult(interp, "wrong # args:  should be \"",
  463.             argv[0], " active file", (char *) NULL);
  464.         return TCL_ERROR;
  465.     }
  466.         fileName = argv [2];
  467.         if (fileName [0] == '~')
  468.             if ((fileName = Tcl_TildeSubst (interp, fileName)) == NULL)
  469.                 return TCL_ERROR;
  470.         if (Tcl_DumpActiveMemory (fileName) != TCL_OK) {
  471.         Tcl_AppendResult(interp, "error accessing ", argv[2], 
  472.             (char *) NULL);
  473.         return TCL_ERROR;
  474.     }
  475.     return TCL_OK;
  476.     }
  477.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  478.         "\":  should be info, init, active, break_on_malloc, ",
  479.         "trace_on_at_malloc, trace, or validate", (char *) NULL);
  480.     return TCL_ERROR;
  481.  
  482. argError:
  483.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  484.         " ", argv[1], "count\"", (char *) NULL);
  485.     return TCL_ERROR;
  486.  
  487. bad_suboption:
  488.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  489.         " ", argv[1], " on|off\"", (char *) NULL);
  490.     return TCL_ERROR;
  491. }
  492.  
  493. /*
  494.  *----------------------------------------------------------------------
  495.  *
  496.  * Tcl_InitMemory --
  497.  *     Initialize the memory command.
  498.  *
  499.  *----------------------------------------------------------------------
  500.  */
  501. void
  502. Tcl_InitMemory(interp)
  503.     Tcl_Interp *interp;
  504. {
  505. Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData)NULL, 
  506.                   (void (*)())NULL);
  507. }
  508.  
  509. #else
  510.  
  511.  
  512. /*
  513.  *----------------------------------------------------------------------
  514.  *
  515.  * Tcl_Ckalloc --
  516.  *     Interface to malloc when TCL_MEM_DEBUG is disabled.  It does check
  517.  *     that memory was actually allocated.
  518.  *
  519.  *----------------------------------------------------------------------
  520.  */
  521. VOID *
  522. Tcl_Ckalloc (size)
  523.     unsigned int size;
  524. {
  525.         char *result;
  526.  
  527.         result = malloc(size);
  528.         if (result == NULL) 
  529.                 panic("unable to alloc %d bytes", size);
  530.         return result;
  531. }
  532.  
  533. /*
  534.  *----------------------------------------------------------------------
  535.  *
  536.  * TckCkfree --
  537.  *     Interface to free when TCL_MEM_DEBUG is disabled.  Done here rather
  538.  *     in the macro to keep some modules from being compiled with 
  539.  *     TCL_MEM_DEBUG enabled and some with it disabled.
  540.  *
  541.  *----------------------------------------------------------------------
  542.  */
  543. void
  544. Tcl_Ckfree (ptr)
  545.     VOID *ptr;
  546. {
  547.         free (ptr);
  548. }
  549.  
  550. /*
  551.  *----------------------------------------------------------------------
  552.  *
  553.  * Tcl_InitMemory --
  554.  *     Dummy initialization for memory command, which is only available 
  555.  *     if TCL_MEM_DEBUG is on.
  556.  *
  557.  *----------------------------------------------------------------------
  558.  */
  559.     /* ARGSUSED */
  560. void
  561. Tcl_InitMemory(interp)
  562.     Tcl_Interp *interp;
  563. {
  564. }
  565.  
  566. #endif
  567.